namespace EyeSoft.Accounting.Italian
{
	using System;

	public class RevertFiscalCode : CalculatedFiscalCode
	{
		private LastNameCode lastNameCode;

		private FirstNameCode firstNameCode;

		private YearCode yearCode;

		private MonthCode monthCode;

		private DayCode dayCode;

		private AreaCode areaCode;

		public RevertFiscalCode(string code)
		{
			IsValid = Validate(code);
		}

		public bool IsValid { get; private set; }

		private bool Validate(string code)
		{
			if (code.Length != 16)
			{
				return false;
			}

			var tokens =
				code
					.Token(FiscalCodeTokens.Lastname, 3)
					.Token(FiscalCodeTokens.Firstname, 3)
					.Token(FiscalCodeTokens.Year, 2)
					.Token(FiscalCodeTokens.Month, 1)
					.Token(FiscalCodeTokens.Day, 2)
					.Token(FiscalCodeTokens.Area, 4)
					.Token(FiscalCodeTokens.Control, 1)
					.Tokens;

			lastNameCode = new LastNameCode(tokens[FiscalCodeTokens.Lastname]);
			LastName = lastNameCode.ToString();
			firstNameCode = new FirstNameCode(tokens[FiscalCodeTokens.Firstname]);
			FirstName = firstNameCode.ToString();

			AssignBirthDateAndSex(tokens);

			areaCode = new AreaCode(tokens[FiscalCodeTokens.Area]);
			Area = areaCode.ToString();

			Control =
				new ControlCode(lastNameCode, firstNameCode, yearCode, monthCode, dayCode, areaCode)
				.ToString();

			return Control[0] == code[15];
		}

		private void AssignBirthDateAndSex(IndexedTokens tokens)
		{
			var year = int.Parse(tokens[FiscalCodeTokens.Year]);
			var month = MonthCode.ToNumber(tokens[FiscalCodeTokens.Month][0]);
			var day = int.Parse(tokens[FiscalCodeTokens.Day]);

			if (day < 40)
			{
				Sex = Sex.Male;
			}
			else
			{
				day -= 40;
				Sex = Sex.Female;
			}

			var dateTime = new DateTime(1900 + year, month, day);

			yearCode = new YearCode(dateTime);
			monthCode = new MonthCode(dateTime);
			dayCode = new DayCode(dateTime, Sex);

			Year = yearCode.ToString();
			Month = monthCode.ToString();
			Day = dayCode.ToString();
		}
	}
}